package com.usian.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.usian.article.feign.AppUserFeign;
import com.usian.article.feign.BehaviorFeign;
import com.usian.article.mapper.*;
import com.usian.article.service.ApArticleService;
import com.usian.model.article.dtos.ArticleHomeDto;
import com.usian.model.article.dtos.ArticleInfoDto;
import com.usian.model.article.pojos.*;
import com.usian.model.article.vo.HotArticleVO;
import com.usian.model.behavior.pojos.ApBehaviorEntry;
import com.usian.model.behavior.pojos.ApLikesBehavior;
import com.usian.model.behavior.pojos.ApUnlikesBehavior;
import com.usian.model.common.constants.ArticleConstans;
import com.usian.model.common.dtos.ResponseResult;
import com.usian.model.common.enums.AppHttpCodeEnum;
import com.usian.model.mess.app.ArticleVisitStreamMess;
import com.usian.model.user.pojos.ApUser;
import com.usian.model.user.pojos.ApUserFollow;
import com.usian.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle>
            implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Value("${fastdfs.url}")
    private String fastServiceUrl;

    // 单页最大加载的文章数
    private final static short MAX_PAGE_SIZE = 50;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;


    @Override
    public ResponseResult load(ArticleHomeDto dto, Short loadtype) {
        if (StringUtils.isEmpty(dto.getTag())){
            dto.setTag(ArticleConstans.DEFAULT_TAG);
        }
        if (dto.getSize()==null || dto.getSize().intValue()==0){
            dto.setSize(10);
        }
        dto.setSize(Math.min(dto.getSize(),MAX_PAGE_SIZE));

        if (dto.getMinBehotTime()==null){
            dto.setMinBehotTime(new Date());
        }
        if (dto.getMaxBehotTime()==null){
            dto.setMaxBehotTime(new Date());
        }

        List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, loadtype);

        ResponseResult responseResult = ResponseResult.okResult(articleList);

        responseResult.setHost(fastServiceUrl);

        return responseResult;
    }

    @Override
    public ResponseResult loadHotArticle(ArticleHomeDto dto, Short loadtype, boolean isFirstPage) {
        if (isFirstPage) {
            // 从缓存中取数据
            String data = redisTemplate.opsForValue().get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            List<HotArticleVO> hotArticleVOList = JSON.parseArray(data, HotArticleVO.class);
            // 3.返回数据
            ResponseResult responseResult = ResponseResult.okResult(hotArticleVOList);

            responseResult.setHost(fastServiceUrl);

            return responseResult;
        }

        return load(dto, loadtype);
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        if (dto==null || dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        Map resultMap = new HashMap<>();


        ApArticleConfig apArticleConfig =
                apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if (apArticleConfig==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (!apArticleConfig.getIsDown() && !apArticleConfig.getIsDelete()){
            ApArticleContent content=
                    apArticleContentMapper.selectOne(
                            Wrappers.<ApArticleContent>lambdaQuery()
                                    .eq(ApArticleContent::getArticleId,dto.getArticleId()));
            resultMap.put("content",content);
        }
        resultMap.put("config",apArticleConfig);
        ResponseResult responseResult = ResponseResult.okResult(resultMap);
        responseResult.setHost(fastServiceUrl);
        return responseResult;
    }


    @Autowired
    private BehaviorFeign behaviorFeign;

    @Autowired
    private ApCollectionMapper apCollectionMapper;

    @Autowired
    private AppUserFeign appUserFeign;

    @Autowired
    private ApAuthorMapper apAuthorMapper;

    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {

        Map<String, Object> resultMap = new HashMap<>();
        boolean islike=false,isunlike=false,iscollection=false,isfollow=false;
        ApUser user = AppThreadLocalUtils.getUser();
        if (user==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        ApBehaviorEntry apBehaviorEntry =
                behaviorFeign.queryEntryByUserIdOrEquipmentId(user.getId(), dto.getEquipmentId());

        ApLikesBehavior apLikesBehavior =
                behaviorFeign.queryLikesByEntryIdAndArticleId(apBehaviorEntry.getId(), dto.getArticleId(), ApLikesBehavior.Type.ARTICLE.getCode());
        if (apLikesBehavior!=null && apLikesBehavior.getOperation()
                !=null&&apLikesBehavior.getOperation()==ApLikesBehavior.Operation.LIKE.getCode()){
            islike =true;
        }
        ApUnlikesBehavior apUnlikesBehavior = behaviorFeign.queryUnlikeByEntryIdAndArticleId(apBehaviorEntry.getId(), dto.getArticleId());
        if (apUnlikesBehavior!=null && apUnlikesBehavior.getType() !=null
                && apUnlikesBehavior.getType()==ApUnlikesBehavior.Type.UNLIKE.getCode()){
            isunlike =true;
        }
        LambdaQueryWrapper<ApCollection> lambdaQueryWrapper =
                Wrappers.<ApCollection>lambdaQuery().eq(ApCollection::getEntryId, apBehaviorEntry.getId()).eq(ApCollection::getArticleId, dto.getArticleId()).eq(ApCollection::getType, ApCollection.Type.ARTICLE);
        Integer collectCount = apCollectionMapper.selectCount(lambdaQueryWrapper);
        if (collectCount==1){
            iscollection=true;
        }

        ApAuthor apAuthor = apAuthorMapper.selectById(dto.getAuthorId());
        ApUserFollow apUserFollow = appUserFeign.queryApUserFollowByUserIdAndFollowId(user.getId(), apAuthor.getUserId());
        if (apUserFollow!=null){
            isfollow =true;
        }


        resultMap.put("islike",islike);
        resultMap.put("isunlike",isunlike);
        resultMap.put("iscollection",iscollection);
        resultMap.put("isfollow",isfollow);

        return ResponseResult.okResult(resultMap);
    }












    @Override
    public void handleHotArticle(ArticleVisitStreamMess mess) {
        // 1.根据文章id查询文章
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle == null) {
            log.error("处理流式计算结果的消息时，文章不存在:{}", JSON.toJSONString(mess));
            return;
        }
        // 2.更新对应的文章表中的  点赞、阅读、收藏、评论   数
        // apArticle.getLikes() + mess.getLike()
        apArticle.setLikes(apArticle.getLikes() == null ? (int)mess.getLike() : apArticle.getLikes() + (int)mess.getLike());
        apArticle.setViews(apArticle.getViews() == null ? (int)mess.getView() : apArticle.getViews() + (int)mess.getView());
        apArticle.setCollection(apArticle.getCollection() == null ? (int)mess.getCollect() : apArticle.getCollection() + (int)mess.getCollect());
        apArticle.setComment(apArticle.getComment() == null ? (int)mess.getComment() : apArticle.getComment() + (int)mess.getComment());
        updateById(apArticle);
        // 3. 重新计算文章分值
        Integer score = computeScore(apArticle);
        score = score * 3;
        // 4.处理redis中的数据
        // 4.1.处理对应频道缓存数据
        String hotArticleStr =
                redisTemplate.opsForValue().get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        handleRedisData(hotArticleStr, score, apArticle, ArticleConstans.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        // 4.2.处理对应推荐缓存数据
        String allHotArticleStr =
                redisTemplate.opsForValue().get(ArticleConstans.HOT_ARTICLE_FIRST_PAGE + ArticleConstans.DEFAULT_TAG);

        handleRedisData(allHotArticleStr, score, apArticle, ArticleConstans.HOT_ARTICLE_FIRST_PAGE + ArticleConstans.DEFAULT_TAG);
    }

    private void handleRedisData(String hotArticleStr, Integer score, ApArticle apArticle, String key){
        // 1.hotArticleStr-->fastJson-->List<HotArticleVO>
        if (StringUtils.isEmpty(hotArticleStr)) {
            return;
        }
        List<HotArticleVO> hotArticleVOList = JSON.parseArray(hotArticleStr, HotArticleVO.class);
        // 2.如果文章已经在推荐列表，只需要更新一下score，重新排序，再放入redis中
        boolean flag = false;
        for (HotArticleVO vo : hotArticleVOList) {
            if (vo.getId().equals(apArticle.getId())) {
                vo.setScore(score);
                flag = true;
                break;
            }
        }
        if (!flag) {
            // 3.如果文章不在推荐列表
            if (hotArticleVOList.size() < 30) {
                // 3.1.推荐列表不足30条，直接将文章存入List<HotArticleVO>，重新排序，再放入redis中
                HotArticleVO hotArticleVO = new HotArticleVO();
                BeanUtils.copyProperties(apArticle, hotArticleVO);
                hotArticleVO.setScore(score);
                hotArticleVOList.add(hotArticleVO);
            } else {
                // 3.2.推荐列表正好30条，取出分值最低的文章，比较，如果当前文章大于分值最低的，替换，重新排序，再放入redis中；如果小于，不处理。
                HotArticleVO minScoreArticle = hotArticleVOList.get(hotArticleVOList.size() - 1);
                if (score > minScoreArticle.getScore()) {
                    // 删除最后一条数据，然后把当前文章数据存入list
                    hotArticleVOList.remove(hotArticleVOList.size() - 1);
                    HotArticleVO hotArticleVO = new HotArticleVO();
                    BeanUtils.copyProperties(apArticle, hotArticleVO);
                    hotArticleVO.setScore(score);
                    hotArticleVOList.add(hotArticleVO);
                }
            }
        }

        hotArticleVOList.sort(new Comparator<HotArticleVO>() {
            @Override
            public int compare(HotArticleVO o1, HotArticleVO o2) {
                return o2.getScore() - o1.getScore();
            }
        });
        redisTemplate.opsForValue().set(key, JSON.toJSONString(hotArticleVOList));
    }




    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        // 阅读
        if (apArticle.getViews() != null) {
            score += apArticle.getViews();
        }
        // 点赞
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstans.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstans.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstans.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }
}
